package com.atguigu.zkb.aspect;



import com.atguigu.zkb.annotation.Cacheable;
import com.atguigu.zkb.constant.CacheAbleConstant;
import com.atguigu.zkb.service.CacheOpsService;
import com.fasterxml.jackson.core.type.TypeReference;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.Expression;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Map;

@Aspect
//@Component
public class CacheAspect {

    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private RBloomFilter rbloomFilter;
    @Autowired
    private CacheOpsService cacheOpsServiceImpl;

    @Around(value = "@annotation(com.atguigu.zkb.annotation.Cacheable)")
    public Object checkCache(ProceedingJoinPoint pjp) throws Throwable {
        //获取目标方法的参数albumId
//        Object[] args = pjp.getArgs();
        //获取目标方法对象与方法注解
        Method method = getMethod(pjp);
        Cacheable methodAnnotation = getMethodAnnotation(method, Cacheable.class);

        String cacheKeyExpression  = methodAnnotation.cacheKey();
        String cacheKey = computeCacheKey(cacheKeyExpression, pjp, String.class);

        String lockKeyExpression = methodAnnotation.distroLockKey();
        String lockKey = computeCacheKey(lockKeyExpression, pjp, String.class);

        String bloomKeyExpression = methodAnnotation.distroBloomKey();
        Long bloomKey = computeCacheKey(bloomKeyExpression, pjp, Long.class);
//        // 定义变量
//        // 定义缓存key
//        String cacheKey = RedisConstant.CACHE_INFO_PREFIX + args[0];
//        // 定义锁key
//        String lockKey = RedisConstant.CACHE_LOCK_SUFFIX + args[0];
        boolean bloomSwitch = methodAnnotation.distroBloomSwitch();
        if (bloomSwitch){
            //查询布隆
            boolean contains = rbloomFilter.contains(bloomKey);
            if (!contains) {
                return null;
            }
        }
//        Type genericReturnType = method.getGenericReturnType();
        Object dataFromCache = cacheOpsServiceImpl.getDataFromCache(cacheKey, new TypeReference<Object>() {
            @Override
            public Type getType() {
                return method.getGenericReturnType();
            }
        });
        if (dataFromCache != null) {
            //缓存命中
            return dataFromCache;
        }

        boolean lockSwitch = methodAnnotation.distroLockSwitch();
        if (!lockSwitch){
            Object objectFromDb = pjp.proceed();
            //同步到缓存中
            cacheOpsServiceImpl.saveDataToCache(cacheKey, objectFromDb);
            return objectFromDb;
        }

        // 缓存未命中
        RLock lock = redissonClient.getLock(lockKey);
        boolean isGetLock = lock.tryLock();//抢锁
        if (isGetLock) {
            //回源数据
            try {
                Object objectFromDb = pjp.proceed();
                //同步到缓存中
                cacheOpsServiceImpl.saveDataToCache(cacheKey, objectFromDb);
                return objectFromDb;
            } finally {
                lock.unlock();
            }
        } else {
            //未抢到锁
            try {
                Thread.sleep(CacheAbleConstant.SYNC_DATA_TIME);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return cacheOpsServiceImpl.getDataFromCache(cacheKey, Map.class);
        }
    }

    /**
     * 获取目标方法
     * @param pjp 目标方法
     * @return 目标方法
     */
    private static Method getMethod(ProceedingJoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        return signature.getMethod();

    }
    /**
     * 获取注解
     */
    private static <T extends Annotation> T getMethodAnnotation(Method method, Class<T> tClass) {
        return method.getAnnotation(tClass);
    }

    /**
     * 计算缓存key
     * @param cacheKeyExpression 表达式
     * @param pjp  目标方法
     * @return 计算结果
     */
    private <T> T computeCacheKey(String cacheKeyExpression, ProceedingJoinPoint pjp, Class<T> valueType) {

        // 1. 创建解析表达式的对象
        SpelExpressionParser spelExpressionParser = new SpelExpressionParser();

        // 2.创建解析上下文对象
        TemplateParserContext templateParserContext = new TemplateParserContext();

        // 3.创建一个计算表达式对象
        StandardEvaluationContext standardEvaluationContext = new StandardEvaluationContext();
        standardEvaluationContext.setVariable("args", pjp.getArgs());

        Expression expression = spelExpressionParser.parseExpression(cacheKeyExpression, templateParserContext);
        return expression.getValue(standardEvaluationContext, valueType);
    }
}

